package com.ymt.bpm.engine.handler.activity.impl;

import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.JSONPath;
import com.ymt.bpm.dao.MDao;
import com.ymt.bpm.dao.Mapper;
import com.ymt.bpm.engine.EngineConst;
import com.ymt.bpm.engine.bpmn.ProcessUtil;
import com.ymt.bpm.engine.handler.activity.AssigneeService;
import com.ymt.bpm.engine.handler.activity.UserTaskDelegate;
import com.ymt.bpm.engine.listener.engine.exception.EngineException;
import com.ymt.bpm.engine.listener.engine.exception.ErrorCode;
import com.ymt.bpm.model.*;
import com.ymt.bpm.service.engine.bpd.BpdVersionCacheService;
import com.ymt.bpm.service.engine.task.FmdIntegrationService;
import com.ymt.bpm.service.engine.task.NotifyService;
import com.ymt.bpm.service.engine.task.TiService;
import com.ymt.bpm.util.Const;
import com.ymt.bpm.util.DateUtil;
import org.activiti.engine.delegate.DelegateTask;
import org.apache.commons.lang.ObjectUtils;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cglib.beans.BeanCopier;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;

/**
 * Created by Johnny on 2016/12/24.
 */
@Component
public class UserTaskDelegateImpl implements UserTaskDelegate {

    private Logger log = LoggerFactory.getLogger(this.getClass());

    @Autowired
    private Environment env;

    @Autowired
    private MDao dao;

    @Autowired
    private BpdVersionCacheService bvcs;

    @Autowired
    private AssigneeService assigneeService;

    @Autowired
    private TiService tis;

    @Autowired
    private FmdIntegrationService fis;

    @Autowired
    private NotifyService ntfSvc;

    @Transactional(EngineConst.TRANSACTION_MANAGER)
    @Override
    public void onCreate(DelegateTask dt) {

        log.debug("onCreate = " + dt.getTaskDefinitionKey());

        boolean jsonDataEnabled = Boolean.parseBoolean(env.getProperty("bpmp.fmd-json-bizdata", "false"));

        //检查是否为起草环节，如果是，则什么都不做，重置DRAFT_ACTIVITY变量
        String draftActivity = dt.getVariable(EngineConst.DRAFT_ACTIVITY, String.class);
        if (dt.getTaskDefinitionKey().equals(draftActivity)) {
            if (log.isDebugEnabled()) {
                log.debug("Task is draftActivity:"+dt.getTaskDefinitionKey()
                        +", skiping rest actions onCreate. executionId="+dt.getExecutionId());
            }
            dt.removeVariable(EngineConst.DRAFT_ACTIVITY);
            return;
        }

        //tenantId
        String tenantId = "".equals(dt.getTenantId()) ? null : dt.getTenantId();

        // 设置引擎办理人
        dt.setAssignee(EngineConst.DEFAULT_USR_ID);

        //操作
        /*int lastOperation = dt.getVariable(EngineConst.PV_LAST_OPERATION, Integer.class);
        if (log.isDebugEnabled()) {
            log.debug("onCreate lastOperation = " + lastOperation); //不使用，并发时不能保证同步
        }*/

        //PI
        String piEid = dt.getProcessInstanceId();
        PPi pi = new PPi();
        PUserRef piUserRef = new PUserRef();
        PPiAuth piAuth = new PPiAuth();
        if (tenantId!=null && StringUtils.isNumeric(tenantId)) {
            pi.setTenantId(Integer.parseInt(tenantId));
            piUserRef.setTenantId(Integer.parseInt(tenantId));
            piAuth.setTenantId(Integer.parseInt(tenantId));
        }
        pi.setPiEid(piEid);
        pi = dao.selectOneByObject(Mapper.PPiMapper, "selectByObj", pi);
        piUserRef.setId(pi.getPiEid());
        piUserRef.setType(PUserRef.TYPE_PROCESS);
        piUserRef.setUserId(pi.getUserId());
        piUserRef = dao.selectOne(Mapper.PUserRefMapper, "selectByPrimaryKey", piUserRef);

        // 查询环节配置
        // 获取版本
        JSONObject version = bvcs.getVersionJson(tenantId, pi.getBpdId(), pi.getVersionid());
        JSONObject bpmnConfig = version.getJSONObject("bpmnConfig");
        JSONObject activityConf = bpmnConfig.getJSONObject(dt.getTaskDefinitionKey());
        if (log.isDebugEnabled()) {
            log.debug("bpmnConfig=" + bpmnConfig);
            log.debug("activityConf=" + activityConf);
        }

        //计算任务到期日期
        String timelineUnit = (String)JSONPath.eval(bpmnConfig, "$.Collaboration_1.timelineUnit");
        String duration = activityConf.getString("duration");
        Date dueTime = ProcessUtil.getDueTime(timelineUnit, duration);

        //获取models
        JSONObject formBinding = activityConf.getJSONObject("formBinding");
        //JSONObject models = fis.getModelsOfForm(formBinding.getString("formid"), formBinding.getString("versionid"));

        //bizdata summary
        JSONObject dataSummary = activityConf.getJSONObject("dataSummary");
        JSONObject summary = new JSONObject();
        if (dataSummary!=null) {
            int slimit = Integer.parseInt(env.getProperty("bpmp.bizdata-summary-limit", "5"));
            if (jsonDataEnabled) {
                int count = 0;
                JSONObject zh = new JSONObject();
                JSONObject en = new JSONObject();
                for (String modelName : dataSummary.keySet()) {
                    if (count>=slimit) {
                        break;
                    }
                    JSONObject attrs = dataSummary.getJSONObject(modelName);
                    PDataM dm = new PDataM();
                    if (tenantId!=null && StringUtils.isNumeric(tenantId)) {
                        dm.setTenantId(Integer.parseInt(tenantId));
                    }
                    dm.setPiEid(pi.getPiEid());
                    dm.setModel(modelName);
                    dm = dao.selectOne(Mapper.PDataMMapper, "selectByPrimaryKey", dm);
                    if (dm!=null) {
                        JSONObject bizData = JSONObject.parseObject(dm.getBizData());
                        for (String colName : attrs.keySet()) {
                            Object raw = bizData.get(colName);
                            String val = ObjectUtils.toString(raw);
                            zh.put(attrs.getJSONObject(colName).getString(Const.LANG_ZH), val);
                            en.put(attrs.getJSONObject(colName).getString(Const.LANG_EN), val);
                            count++;
                        }
                    }
                }
                summary.put(Const.LANG_ZH, zh);
                summary.put(Const.LANG_EN, en);
            } else {
                int count = 0;
                JSONObject zh = new JSONObject();
                JSONObject en = new JSONObject();
                for (String modelName : dataSummary.keySet()) {
                    if (count>=slimit) {
                        break;
                    }
                    JSONObject attrs = dataSummary.getJSONObject(modelName);
                    StringBuilder sql = new StringBuilder();
                    sql.append("select * from ")
                            .append(modelName)
                            .append(" where TS_ID='")
                            .append(pi.getPiEid())
                            .append("'");
                    Map<String, String> map = new HashMap<String, String>();
                    map.put("sql", sql.toString());
                    Map<String, Object> result = dao.selectOne(Mapper.CustomSQLMapper, "selectOne", map);
                    if (result!=null) {
                        for (String colName : attrs.keySet()) {
                            Object raw = result.get(colName);
                            String val = null;
                            if (raw instanceof Date) {
                                continue;
                            } else {
                                val = ObjectUtils.toString(raw);
                            }
                            zh.put(attrs.getJSONObject(colName).getString(Const.LANG_ZH), val);
                            en.put(attrs.getJSONObject(colName).getString(Const.LANG_EN), val);
                            count++;
                        }
                    }
                }
                summary.put(Const.LANG_ZH, zh);
                summary.put(Const.LANG_EN, en);
            }
        }

        //建立待办bean
        PTaskTodo todo = new PTaskTodo();
        if (tenantId!=null && StringUtils.isNumeric(tenantId)) {
            todo.setTenantId(Integer.parseInt(tenantId));
        }
        //todo.setTiId("TID-"+UUID.randomUUID());
        todo.setTiEId(dt.getId());
        todo.setPiEid(piEid);
        todo.setPiId(pi.getPiId());
        todo.setPurpose(PTaskTodo.PURPOSE_NONE);
        todo.setStatus(PTaskTodo.STATUS_NEW);
        todo.setActivityId(dt.getTaskDefinitionKey());
        todo.setActivityName(dt.getName());
        todo.setCreateTime(DateUtil.getGMTDate());
        todo.setDueTime(dueTime);
        todo.setSummary(summary.toJSONString());
        todo.setPriority(PTaskTodo.PRIORITY_NORMAL);    //默认普通优先级，后面根据配置修改
        String priority = activityConf.getString("priority");
        if (priority!=null && priority.length()>0 && StringUtils.isNumeric(priority)) {
            todo.setPriority(Integer.parseInt(priority));
        }

        //调取配置服务，查询任务办理人
        List<JSONObject> users = assigneeService.getAssigneeByTaskConfig(tenantId, activityConf, dt, pi, piUserRef);
        log.debug("------------------------------------set assignee begin");
        if(null != users && users.size() > 0){
            if(users.size() == 1){//单人签核
                JSONObject user = users.get(0);
                if (log.isDebugEnabled()) log.debug("Single activity...userinfo="+user);
                todo.setTiId("TID-"+UUID.randomUUID());
                todo.settType(PTaskTodo.TYPE_NORMAL);
                todo.setUserId(user.getString("userId"));
                todo.setUserName(user.getString("userName"));
                if (user.containsKey("dUserId")) {  //代理用户
                    todo.setdUserId(user.getString("dUserId"));
                    todo.setdUserName(user.getString("dUserName"));
                }

                //办理人信息
                PUserRef ur = new PUserRef();
                if (tenantId!=null && StringUtils.isNumeric(tenantId)) {
                    ur.setTenantId(Integer.parseInt(tenantId));
                }
                ur.setId(todo.getTiId());
                ur.setType(PUserRef.TYPE_TASK); // 任务
                ur.setUserId(user.getString("userId"));
                ur.setUserName(user.getString("userName"));
                ur.setDeptId(user.getString("deptId"));
                ur.setDeptName(user.getString("deptName"));

                dao.insert(Mapper.PTaskTodoMapper, "insertSelective", todo);
                dao.insert(Mapper.PUserRefMapper, "insertSelective", ur);

                // 相邻任务环节处理人相同自动跳过
                if ("1".equals(activityConf.get("skipOnSameAssignee"))) {
                    // 查询上一任务环节处理人
                    // 1 查询上一环节任务办理人
                    Map<String, Object> pMap1 = new HashMap<String, Object>();
                    pMap1.put(Const.TENANT_ID, tenantId);
                    pMap1.put("piId", pi.getPiId());
                    pMap1.put("piEid", pi.getPiEid());
                    pMap1.put("activityId", todo.getActivityId());
                    List<String> userIds = dao.selectList(Mapper.AssigneeMapper, "selectPreTaskUser", pMap1);
                    if (userIds!=null && userIds.size()>0 && userIds.indexOf(todo.getUserId())>=0) {
                        JSONObject cpParam = new JSONObject();
                        cpParam.put("operation", PTaskDone.OPERATION_SKIP_SAMEASSIGNEE);
                        tis.completeTask(tenantId, todo, bpmnConfig, cpParam, true);
                        return;
                    }
                }

                // 写任务消息
                ntfSvc.recordTodoNotify(tenantId, bpmnConfig, activityConf, todo, pi);
                log.debug("-----------------------------------single assign end");


            }else{//多人签核
                log.debug("Multi-instance activity...userinfo="+users);
                List<PTaskTodo> todos = new ArrayList<PTaskTodo>();
                List<PUserRef> urs = new ArrayList<PUserRef>();
                BeanCopier copier = BeanCopier.create(PTaskTodo.class, PTaskTodo.class, false);

                String assignType = activityConf.getString("assignType");
                String tType = PTaskTodo.TYPE_COMPETE;
                if ("multiple".equalsIgnoreCase(assignType)) {
                    tType = PTaskTodo.TYPE_CONSIGN;
                }

                for (JSONObject user : users) {

                    PTaskTodo todo1 = new PTaskTodo();
                    copier.copy(todo, todo1, null); //复制bean
                    todo1.setTiId("TID-"+UUID.randomUUID());
                    todo1.settType(tType);
                    todo1.setUserId(user.getString("userId"));
                    todo1.setUserName(user.getString("userName"));
                    if (user.containsKey("dUserId")) {  //代理用户
                        todo1.setdUserId(user.getString("dUserId"));
                        todo1.setdUserName(user.getString("dUserName"));
                    }

                    //办理人信息
                    PUserRef ur = new PUserRef();
                    if (tenantId!=null && StringUtils.isNumeric(tenantId)) ur.setTenantId(Integer.parseInt(tenantId));
                    ur.setId(todo1.getTiId());
                    ur.setType(PUserRef.TYPE_TASK); // 任务
                    ur.setUserId(user.getString("userId"));
                    ur.setUserName(user.getString("userName"));
                    ur.setDeptId(user.getString("deptId"));
                    ur.setDeptName(user.getString("deptName"));

                    todos.add(todo1);
                    urs.add(ur);

                    // 写任务消息
                    ntfSvc.recordTodoNotify(tenantId, bpmnConfig, activityConf, todo1, pi);
                }
                dao.batchInsert(Mapper.PTaskTodoMapper, "insertSelective", todos);
                dao.batchInsert(Mapper.PUserRefMapper, "insertSelective", urs);
                log.debug("-------------------------------------multiple assign end, "+todos.size() + " todos inserted.");
            }
            log.debug("-----------------------------------------assignee finding & setting end");

        }else{//办理人设置查询为空
            // 找不到任务办理人自动跳过(建议找不到任务办理人自动跳过复选框，默认为选中；如果 不选可设置为自动跳过或指给超级管理员处理)
            if ("1".equals(activityConf.get("skipOnVoidAssignee"))) {
                // 插入办理人信息
                todo.setTiId("TID-"+UUID.randomUUID());
                todo.settType(PTaskTodo.TYPE_NORMAL);
                todo.setUserId(EngineConst.DEFAULT_USR_ID);
                todo.setUserName(EngineConst.DEFAULT_USR_NAME);

                PUserRef ur = new PUserRef();
                if (tenantId!=null && StringUtils.isNumeric(tenantId)) ur.setTenantId(Integer.parseInt(tenantId));
                ur.setId(todo.getTiId());
                ur.setType(PUserRef.TYPE_TASK); // 任务
                ur.setUserId(EngineConst.DEFAULT_USR_ID);
                ur.setUserName(EngineConst.DEFAULT_USR_NAME);

                dao.insert(Mapper.PTaskTodoMapper, "insertSelective", todo);
                dao.insert(Mapper.PUserRefMapper, "insertSelective", ur);

                JSONObject cpParam = new JSONObject();
                cpParam.put("operation", PTaskDone.OPERATION_SKIP_NOASSIGNEE);
                tis.completeTask(tenantId, todo, bpmnConfig, cpParam, true);
                return;
            }
            log.error("assignee not found for pi:"+pi.getPiEid()+", activity:"+dt.getTaskDefinitionKey()+", name:"+dt.getName());
            throw new RuntimeException(new EngineException(ErrorCode.ATVITIY_FAIL,
                    "assignee not found for pi:"+pi.getPiEid()+", activity:"+dt.getTaskDefinitionKey()+", name:"+dt.getName()));
        }
        log.debug("---------------------------------------assignee setting end");

        //授权流程
        if (users!=null && users.size()>0) {
            piAuth.setPiId(pi.getPiId());
            List<PPiAuth> piAuths = new ArrayList<PPiAuth>();
            BeanCopier copierPa = BeanCopier.create(PPiAuth.class, PPiAuth.class, false);
            for (JSONObject user : users) {
                PPiAuth aUser = new PPiAuth();  //办理人
                copierPa.copy(piAuth, aUser, null);
                aUser.setAuthType(PPiAuth.AUTH_TYPE_TASKOWNER);
                aUser.setUserId(user.getString("userId"));
                piAuths.add(aUser);
                if (user.containsKey("dUserId")) {  //代理用户
                    PPiAuth aDUser = new PPiAuth();
                    copierPa.copy(piAuth, aDUser, null);
                    aDUser.setAuthType(PPiAuth.AUTH_TYPE_DELEGATOR);
                    aDUser.setUserId(user.getString("dUserId"));
                    piAuths.add(aDUser);
                }
            }
            dao.batchInsert(Mapper.PPiAuthMapper, "insertOrUpdate", piAuths);
        }

        //根据任务环节配置和流程变量，更新任务环节绑定表单的模型表数据
        JSONObject processVar = JSONObject.parseObject(dt.getVariable("processVar").toString());
        JSONObject variables = processVar.getJSONObject("variables");
        if(activityConf.containsKey("dataMapping")&&activityConf.getJSONObject("dataMapping").containsKey("input")){
            JSONObject input = activityConf.getJSONObject("dataMapping").getJSONObject("input");
            if(!variables.isEmpty() && !input.isEmpty()){
                ProcessUtil.updateBODataByTaskConfigAndProcessVar(tenantId,
                        jsonDataEnabled,
                        env.getProperty("mybatisplugin.dialect"),
                        dao, input, variables, pi.getPiEid());
            }else{
                log.debug("process doesn't have processVar or input is null, proessInstance is:"+pi.getTitle());
            }
        }

    }

    @Override
    public void onAssignment(DelegateTask dt) {

    }

    @Override
    public void onComplete(DelegateTask dt) {

    }

    @Override
    public void onDelete(DelegateTask dt) {

    }

}
